package com.orange.lockscreen.layers.slider;

import android.graphics.Rect;
import android.os.Handler;
import android.os.Message;
import android.view.MotionEvent;

import com.orange.lockscreen.SlatHashMap;

public class LSGestureDetector {

	public interface OnGestureListener {
		// boolean onSingleTapConfirm(MotionEvent event);

		// boolean onDown(MotionEvent event);

		boolean onSwipe(MotionEvent event);

		boolean onUp(MotionEvent event);

		boolean onDoubleTap(MotionEvent event);
	}

	// private static final String TAG = "LSGestureDetector";

	private static final int	TAP						= 1;
	private static final int	TAP_TIMEOUT_L			= 15;
	private static final int	TAP_TIMEOUT_H			= 500;
	private static final int	TAP_INTERVAL			= 100;

	// private int mDoubleTapSlopSquare = 100 * 100;
	// private int mTouchSlopSquare = 16 * 16;
	private int					mBiggerTouchSlopSquare	= 30 * 30;

	private Handler				mHandler;
	private OnGestureListener	mListener;

	private boolean				mAlwaysInTapRegion;
	private boolean				mAlwaysInBiggerTapRegion;

	private MotionEvent			mPreviewDownEvent;
	private MotionEvent			mPreviewUpEvent;
	private boolean				mIsDoubleTapping;

	private boolean				mStillDown;

	public LSGestureDetector(OnGestureListener listener) {
		this(listener, null);
	}

	public LSGestureDetector(OnGestureListener listener, Handler handler) {
		if (handler != null) {
			mHandler = new GestureHandler(handler);
		} else {
			mHandler = new GestureHandler();
		}
		mListener = listener;
		init();
	}

	private void init() {
		if (mListener == null) {
			throw new NullPointerException("OnGestureListener must not be null");
		}
	}

	public boolean onTouchEvent(MotionEvent event) {
		final int action = event.getAction();
		final float x = event.getX();
		final float y = event.getY();

		boolean handled = false;

		switch (action & MotionEvent.ACTION_MASK) {
		case MotionEvent.ACTION_DOWN:

			MotionEvent downEvent = MotionEvent.obtain(event);

			boolean hadTapMessage = mHandler.hasMessages(TAP);
			if (hadTapMessage) {
				long interval = event.getEventTime()
						- mPreviewDownEvent.getEventTime();
				if (interval < TAP_INTERVAL) {
					return true;
				}
			}
			if (hadTapMessage) {
				mHandler.removeMessages(TAP);
			}

			if ((mPreviewDownEvent != null)
					&& (mPreviewUpEvent != null)
					&& hadTapMessage
					&& isConsideredDoubleTap(mPreviewDownEvent,
							mPreviewUpEvent, event)) {
				mIsDoubleTapping = true;
			} else {
				mHandler.sendEmptyMessageDelayed(TAP, TAP_TIMEOUT_H);
			}

			if (mPreviewDownEvent != null) {
				mPreviewDownEvent.recycle();
			}
			mPreviewDownEvent = downEvent;
			mStillDown = true;
			mAlwaysInTapRegion = true;
			mAlwaysInBiggerTapRegion = true;

			// handled |= mListener.onDown(event);
			break;
		case MotionEvent.ACTION_MOVE:
			if (mAlwaysInTapRegion) {
				final int deltaX = (int) (x - mPreviewDownEvent.getX());
				final int deltaY = (int) (y - mPreviewDownEvent.getY());
				int distance = (deltaX * deltaX) + (deltaY * deltaY);
				if (distance > mBiggerTouchSlopSquare) {
					mAlwaysInTapRegion = false;
					mAlwaysInBiggerTapRegion = false;
					mHandler.removeMessages(TAP);
				}
			}
			break;
		case MotionEvent.ACTION_UP:
			mStillDown = false;
			MotionEvent upEvent = MotionEvent.obtain(event);
			if (mIsDoubleTapping) {
				handled |= mListener.onDoubleTap(event);
			} else {
				boolean hadTapMSG = mHandler.hasMessages(TAP);
				if (!hadTapMSG) {
					handled |= mListener.onSwipe(event);
				} else {
//					handled |= mListener.onUp(mPreviewDownEvent);
				    //modify by gaoge 2012-03-16 for test purpose
					handled |= mListener.onDoubleTap(event);
				}
			}
			if (mPreviewUpEvent != null) {
				mPreviewUpEvent.recycle();
			}
			mPreviewUpEvent = upEvent;
			mIsDoubleTapping = false;
			break;
		case MotionEvent.ACTION_CANCEL:
			cancel();
			break;
		default:
			break;
		}
		return handled;
	}

	private void cancel() {
		mHandler.removeMessages(TAP);
		mIsDoubleTapping = false;
		mStillDown = false;
	}

	public void release() {
		if (mPreviewDownEvent != null) {
			mPreviewDownEvent.recycle();
		}
		if (mPreviewUpEvent != null) {
			mPreviewUpEvent.recycle();
		}
	}

	private boolean isConsideredDoubleTap(MotionEvent firstDown,
			MotionEvent firstUp, MotionEvent secondDown) {
		if (!mAlwaysInBiggerTapRegion) {
			return false;
		}

		long interval = secondDown.getEventTime() - firstUp.getEventTime();
		if (interval > TAP_TIMEOUT_H || interval <= TAP_TIMEOUT_L) {
			return false;
		}

		boolean isDoubleTap = false;
		int top = 0;
		int height = 0;
		Rect rect = null;
		final int count = SlatHashMap.getCount();
		for (int i = 0; i < count; i++) {
			top = SlatHashMap.getSlatTop(i);
			height = SlatHashMap.getSlatHeight(i);
			rect = new Rect(0, top, SlatHashMap.getScreenWidth(), top + height);
			if (rect.contains((int) firstDown.getX(), (int) firstDown.getY())
					&& rect.contains((int) secondDown.getX(),
							(int) secondDown.getY())) {
				isDoubleTap = true;
				break;
			}
		}
		return isDoubleTap;
		// int deltaX = (int) secondDown.getX() - (int) firstDown.getX();
		// int deltaY = (int) secondDown.getY() - (int) firstDown.getY();
		// return ((deltaX * deltaX + deltaY * deltaY) < mDoubleTapSlopSquare);
	}

	private class GestureHandler extends Handler {
		GestureHandler() {
			super();
		}

		GestureHandler(Handler handler) {
			super(handler.getLooper());
		}

		@Override
		public void handleMessage(Message msg) {
			switch (msg.what) {
			case TAP:
				if (mListener != null && !mStillDown) {
					// mListener.onSingleTapConfirm(mPreviewUpEvent);
				}
				break;
			default:
				throw new RuntimeException("Unknown message " + msg);
			}
		}
	}
}
